查看原文
其他

如何基于 Nacos 和 Sentinel ,实现灰度路由和流量防护一体化

IT牧场 2021-08-09

The following article is from 阿里巴巴中间件 Author ​任浩军 王伟华

点击上方"IT牧场",选择"设为星标"

技术干货每日送达!


基于 Nacos 和 Sentinel ,实现灰度路由和流量防护一体化的解决方案,发布在最新的 Nepxion Discovery 5.4.0 版本,具体参考:

源码主页,请访问 :
https://github.com/Nepxion/Discovery

指南主页,请访问: 
https://github.com/Nepxion/DiscoveryGuide

文档主页,请访问:
https://pan.baidu.com/s/1i57rXaNKPuhGRqZ2MONZOA

 Nepxion Discovery 框架在实现灰度发布和路由功能前提下,结合 Nacos 和 Sentinel ,对流量再实施一层防护措施,更能达到企业级的流量安全控制的目的。它的功能包括:
  • 封装远程配置中心和本地规则文件的读取逻辑,即优先读取远程配置,如果不存在或者规则错误,则读取本地规则文件。动态实现远程配置中心对于规则的热刷新;

  • 封装 NacosDataSource 和 ApolloDataSource ,支持 Nacos 和 Apollo 两个远程配置中心,零代码实现 Sentinel 功能。更多的远程配置中心,请参照 Sentinel 官方的 DataSource 并自行集成;

  • 支持原生的流控规则、降级规则、授权规则、系统规则、热点参数流控规则;

  • 支持扩展 LimitApp 的机制,通过动态的 Http Header 方式实现组合式防护机制,包括基于服务名、基于灰度组、基于灰度版本、基于灰度区域、基于机器地址和端口等防护机制,支持自定义任意的业务参数组合实现该功能,例如,根据传入的微服务灰度版本号+用户名,组合在一起进行熔断;

  • 支持微服务侧 Actuator、Swagger 和 Rest 三种方式的规则写入;

  • 支持控制台侧基于微服务名的 Actuator、Swagger 和 Rest 三种方式的批量规则写入;

  • 支持开关关闭上述功能

    spring.application.strategy.sentinel.enabled=true ,默认是关闭的。


[Nacos] 阿里巴巴中间件部门开发的新一代集服务注册发现中心和配置中心为一体的中间件。它是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施,支持几乎所有主流类型的“服务”的发现、配置和管理,更敏捷和容易地构建、交付和管理微服务平台。

[Sentinel] 阿里巴巴中间件部门开发的新一代以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性的分布式系统的流量防卫兵。它承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。


环境搭建及依赖引入



服务端在 Discovery 框架原有依赖的基础上,再引入如下依赖:

<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-strategy-starter-service-sentinel</artifactId>
<version>${discovery.version}</version>
</dependency>

<dependency>
<groupId>com.nepxion</groupId>
<artifactId>discovery-plugin-strategy-sentinel-starter-nacos</artifactId>
<!-- <artifactId>discovery-plugin-strategy-sentinel-starter-apollo</artifactId> -->
<version>${discovery.version}</version>
</dependency>



原生 Sentinel 注解



参照下面代码,为接口方法增加 @SentinelResource 注解, value为sentinel-resource , blockHandler 和 fallback 是防护其作用后需要执行的方法:

@RestController
@ConditionalOnProperty(name = DiscoveryConstant.SPRING_APPLICATION_NAME, havingValue = "discovery-guide-service-b")
public class BFeignImpl extends AbstractFeignImpl implements BFeign {
private static final Logger LOG = LoggerFactory.getLogger(BFeignImpl.class);

@Override
@SentinelResource(value = "sentinel-resource", blockHandler = "handleBlock", fallback = "handleFallback")
public String invoke(@PathVariable(value = "value") String value) {
value = doInvoke(value);

LOG.info("调用路径:{}", value);

return value;
}

public String handleBlock(String value, BlockException e) {
return value + "-> B server sentinel block, cause=" + e.getClass().getName() + ", rule=" + e.getRule() + ", limitApp=" + e.getRuleLimitApp();
}

public String handleFallback(String value) {
return value + "-> B server sentinel fallback";
}
}



原生 Sentinel 规则



 Sentinel 在配置中心订阅的 Key 格式,如下:

1. Nacos的Key格式:Group为元数据中配置的[组名],Data Id为[服务名]-[规则类型]
2. Apollo的Key格式:[组名]-[服务名]-[规则类型]
 Sentinel 规则的用法,请参照 Sentinel 官方文档。

流控规则


增加服务 discovery-guide-service-b 的规则,Group 为 discovery-guide-group , Data Id 为 discovery-guide-service-b-sentinel-flow ,规则内容如下:
[
{
"resource": "sentinel-resource",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"refResource": null,
"controlBehavior": 0,
"warmUpPeriodSec": 10,
"maxQueueingTimeMs": 500,
"clusterMode": false,
"clusterConfig": null
}
]

如图所示:


降级规则


增加服务 discovery-guide-service-b 的规则,Group 为 discovery-guide-group , Data Id 为 discovery-guide-service-b-sentinel-degrade ,规则内容如下:

[
{
"resource": "sentinel-resource",
"limitApp": "default",
"count": 2,
"timeWindow": 10,
"grade": 0,
"passCount": 0
}
]

如图所示:


授权规则


增加服务discovery-guide-service-b的规则,Group为discovery-guide-group,Data Id为discovery-guide-service-b-sentinel-authority,规则内容如下:

[
{
"resource": "sentinel-resource",
"limitApp": "discovery-guide-service-a",
"strategy": 0
}
]

如图所示:


系统规则


增加服务 discovery-guide-service-b 的规则, Group 为 discovery-guide-group ,Data Id 为 discovery-guide-service-b-sentinel-system ,规则内容如下:

[
{
"resource": null,
"limitApp": null,
"highestSystemLoad": -1.0,
"highestCpuUsage": -1.0,
"qps": 200.0,
"avgRt": -1,
"maxThread": -1
}
]

如图所示:


热点参数流控规则


增加服务 discovery-guide-service-b 的规则,Group 为 discovery-guide-group , Data Id 为 discovery-guide-service-b-sentinel-param-flow ,规则内容如下:

[
{
"resource": "sentinel-resource",
"limitApp": "default",
"grade": 1,
"paramIdx": 0,
"count": 1,
"controlBehavior": 0,
"maxQueueingTimeMs": 0,
"burstCount": 0,
"durationInSec": 1,
"paramFlowItemList": [],
"clusterMode": false
}
]

如图所示:



基于灰度路由和 Sentinel-LimitApp 扩展的防护机制



该方式对于上面 5 种规则都有效,这里以授权规则展开阐述。

授权规则中, limitApp ,如果有多个,可以通过“,”分隔。"strategy": 0  表示白名单,"strategy": 1  表示黑名单。

基于服务名的防护机制


修改配置项 Sentinel Request Origin Key 为服务名的 Header 名称,修改授权规则中 limitApp 为对应的服务名,可实现基于服务名的防护机制。

配置项,该配置项默认为 n-d-service-id ,可以不配置。

spring.application.strategy.service.sentinel.request.origin.key=n-d-service-id

增加服务 discovery-guide-service-b 的规则, Group 为 discovery-guide-group , Data Id 为 discovery-guide-service-b-sentinel-authority ,规则内容如下,表示所有 discovery-guide-service-a 服务允许访问 discovery-guide-service-b 服务。
[
{
"resource": "sentinel-resource",
"limitApp": "discovery-guide-service-a",
"strategy": 0
}
]


基于灰度组的防护机制


修改配置项 Sentinel Request Origin Key 为灰度组的 Header 名称,修改授权规则中 limitApp 为对应的组名,可实现基于组名的防护机制。

配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-group
增加服务 discovery-guide-service-b 的规则,Group 为 discovery-guide-group , Data Id 为 discovery-guide-service-b-sentinel-authority ,规则内容如下,表示隶属 my-group 组的所有服务都允许访问服务 discovery-guide-service-b 。
[
{
"resource": "sentinel-resource",
"limitApp": "my-group",
"strategy": 0
}
]


基于灰度版本的防护机制


修改配置项 Sentinel Request Origin Key 为灰度版本的 Header 名称,修改授权规则中 limitApp 为对应的版本,可实现基于版本的防护机制。

配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-version
增加服务 discovery-guide-service-b 的规则,Group 为 discovery-guide-group ,Data Id 为 discovery-guide-service-b-sentinel-authority ,规则内容如下,表示版本为 1.0  的所有服务都允许访问服务 discovery-guide-service-b 。
[
{
"resource": "sentinel-resource",
"limitApp": "1.0",
"strategy": 0
}
]


基于灰度区域的防护机制


修改配置项 Sentinel Request Origin Key 为灰度区域的 Header 名称,修改授权规则中 limitApp 为对应的区域,可实现基于区域的防护机制。

配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-region
增加服务 discovery-guide-service-b 的规则,Group 为 discovery-guide-group ,Data Id 为 discovery-guide-service-b-sentinel-authority ,规则内容如下,表示区域为 dev 的所有服务都允许访问服务 discovery-guide-service-b 。
[
{
"resource": "sentinel-resource",
"limitApp": "dev",
"strategy": 0
}
]


基于机器地址和端口的防护机制


修改配置项 Sentinel Request Origin Key 为灰度区域的 Header 名称,修改授权规则中 limitApp 为对应的区域值,可实现基于机器地址和端口的防护机制。

配置项
spring.application.strategy.service.sentinel.request.origin.key=n-d-service-address
增加服务 discovery-guide-service-b 的规则,Group 为 discovery-guide-group ,Data Id 为 discovery-guide-service-b-sentinel-authority ,规则内容如下,表示地址和端口为 192.168.0.88:8081 和 192.168.0.88:8082 的服务都允许访问服务 discovery-guide-service-b 。
[
{
"resource": "sentinel-resource",
"limitApp": "192.168.0.88:8081,192.168.0.88:8082",
"strategy": 0
}
]


自定义业务参数的组合式防护机制


通过适配类实现自定义业务参数的组合式防护机制。
// 版本号+用户名,实现组合式熔断
public class MyServiceSentinelRequestOriginAdapter extends DefaultServiceSentinelRequestOriginAdapter {
@Override
public String parseOrigin(HttpServletRequest request) {
String version = request.getHeader(DiscoveryConstant.N_D_SERVICE_VERSION);
String user = request.getHeader("user");

return version + "&" + user;
}
}
在配置类里 @Bean 方式进行适配类创建
@Bean
public ServiceSentinelRequestOriginAdapter ServiceSentinelRequestOriginAdapter() {
return new MyServiceSentinelRequestOriginAdapter();
}
增加服务 discovery-guide-service-b 的规则,Group 为 discovery-guide-group , Data Id 为 discovery-guide-service-b-sentinel-authority ,规则内容如下,表示版本为 1.0 且传入的 Http Header 的 user=zhangsan ,同时满足这两个条件下的所有服务都允许访问服务 discovery-guide-service-b 。
[
{
"resource": "sentinel-resource",
"limitApp": "1.0&zhangsan",
"strategy": 0
}
]
运行效果:当传递的 Http Header 中 user=zhangsan ,当全链路调用中, API 网关负载均衡 discovery-guide-service-a 服务到 1.0 版本后再去调用 discovery-guide-service-b 服务,最终调用成功。

如图所示:


当传递的 Http Header 中 user=lisi ,不满足条件,最终调用在 discovery-guide-service-b 服务端被拒绝掉。

如图所示:


当传递的 Http Header 中 user=zhangsan ,满足条件之一,当全链路调用中, API 网关负载均衡 discovery-guide-service-a 服务到 1.1 版本后再去调用 discovery-guide-service-b 服务,不满足 version=1.0 的条件,最终调用在 discovery-guide-service-b 服务端被拒绝掉。

如图所示:



基于 Swagger 的 Sentinel 规则推送



分为基于单个服务实例和基于服务名对应的多个服务实例的 Sentinel 规则推送。

基于单个服务实例的 Sentinel 规则推送


直接访问该服务实例的 Swagger 主页即可。

如图所示:


基于服务名对应的多个服务实例的 Sentinel 规则推送


需要开启 discovery-console 服务,并访问其 Swagger 主页即可。

如图所示:


本文作者:
任浩军, 10 多年开源经历,Github ID:@HaojunRen,Nepxion 开源社区创始人,Nacos Group Member,Spring Cloud Alibaba & Nacos & Sentinel Committer ,曾就职于平安银行平台架构部,负责银行 PaaS 系统基础服务框架研发。
王伟华, 10 余年 Java 开发,Github ID:@vipweihua,对微服务架构研究多年,当前更多关注于微服务中的网关、限流熔断、灰度路由等,现就职于平安银行平台架构部,从事银行 PaaS 系统基础服务框架研发。

本文缩略图:icon by 阿本

干货分享

最近将个人学习笔记整理成册,使用PDF分享。关注我,回复如下代码,即可获得百度盘地址,无套路领取!

•001:《Java并发与高并发解决方案》学习笔记;•002:《深入JVM内核——原理、诊断与优化》学习笔记;•003:《Java面试宝典》•004:《Docker开源书》•005:《Kubernetes开源书》•006:《DDD速成(领域驱动设计速成)》•007:全部•008:加技术讨论群

近期热文

JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解如何减少长时间的 GC 停顿?代码调试最佳实践代码编排架构三部曲简述超全面的 MySQL优化 面试解析让人头大的各种锁,从这里让你思绪清晰


想知道更多?长按/扫码关注我吧↓↓↓>>>技术讨论群<<<喜欢就点个"在看"呗^_^

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存